﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using System.Reflection;

namespace swfupload
{
    /// <summary>
    /// 文件上传类
    /// </summary>
    /// <remarks>
    /// Created By :王彦忠  
    /// Created Date：2011-9-13
    /// Modify By:
    /// Modify Date:
    /// Modify Reason:
    /// </remarks>
    public class UploadFile
    {
        /// <summary>
        /// 存放路径
        /// </summary>
        public string strSavePath { get; set; }

        /// <summary>
        /// 文件类型
        /// </summary>
        public string strFileExt { get; set; }

        /// <summary>
        /// 文件大小
        /// </summary>
        public int iFileLength { set; get; }

        public HttpPostedFile fileUpload { set; get; }
        FtpUpload ft;

        public UploadFile()
        {
            ft = new FtpUpload();
        }
        public UploadFile(string ftpServerIP, string ftpUserID, string ftpPassword, string ftpPort)
        {
            ft = new FtpUpload(ftpServerIP, ftpUserID, ftpPassword, ftpPort);
        }

        #region 文件上传
        /// <summary>
        /// 文件上传
        /// </summary>
        /// <remarks>
        /// Created By :王彦忠  
        /// Created Date：2011-9-14
        /// Modify By:
        /// Modify Date:
        /// Modify Reason:
        /// </remarks>
        /// <param name="file">文件</param>
        /// <param name="strPathAndName">文件存放路径和保存后的文件名</param>
        /// <param name="bUploadType">是否启用断点续传</param>
        /// <param name="strFileType">允许上传的文件类型 必须用分号分隔 例如.jpg;.gif</param>
        /// <param name="iFileLength">允许上传的最大文件 单位：字节</param>
        /// <param name="strInfo">返回的提示信息</param>
        /// <returns>True:上传成功 Flase:上传失败</returns>
        public bool Upload(HttpPostedFileBase file, string strPathAndName, bool bUploadType, string strFileType, int iFileLength, ref string strInfo)
        {
            bool bFlag = true; //返回类型 
            this.strFileExt = strFileType;

            string strExt = file.FileName.Substring(file.FileName.LastIndexOf('.')); //文件扩展名

            //检测文件类型
            if (!CheckFileType(strExt))
            {
                bFlag = false;
                strInfo = "上传文件类型错误！建议上传文件类型为" + strFileType;
                return bFlag;
            }
            //检测文件大小

            if (file.ContentLength > iFileLength)
            {
                bFlag = false;
                strInfo = "上传文件大小已经超出规定大小！建议上传文件大小为" + iFileLength / 1024 / 1024 + "M";
                return bFlag;
            }

            //检测存放目录
            string strFileName = string.Empty;
            string strFolder = string.Empty;
            CreateFolderAndGetFileName(strPathAndName, strExt, ref strFolder, ref strFileName);
            //开始存放文件
            try
            {
                if (!bUploadType)
                {
                    file.SaveAs(strFolder.Trim('\\') + @"\" + strFileName);
                }
                else
                {
                    var provider = (IServiceProvider)HttpContext.Current;
                    var workerRequest = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
                    var encoding = HttpContext.Current.Request.ContentEncoding;
                    var processor = new UploadProcessor(workerRequest);
                    processor.StreamToDisk(HttpContext.Current, encoding, strFolder);
                }
                strInfo = strPathAndName;
            }
            catch (Exception ex)
            {
                bFlag = false;
                strInfo = ex.Message;
            }
            return bFlag;
        }

        public bool UploadFTP(HttpPostedFileBase file, string strFileType, int iFileLength, int Width, int Height, string Path, ref string strInfo)
        {
            return UploadFTP(file, strFileType, iFileLength, Width, Height, Path, string.Empty, ref strInfo);
        }
        public bool UploadFTP(HttpPostedFileBase file, string strFileType, int iFileLength, int Width, int Height, string Path, string fileName, ref string strInfo)
        {
            if (strFileType.ToLower().Split(';').Contains(System.IO.Path.GetExtension(file.FileName).ToLower()) == false)
            {
                strInfo = "上传文件类型不匹配";
                return false;
            }
            if (file.ContentLength > iFileLength)
            {
                strInfo = "上传文件大小已经超出规定大小！建议上传文件大小为" + iFileLength / 1024 / 1024 + "M";
                return false;
            }
            try
            {
                string strName = "";
                if (!string.IsNullOrEmpty(fileName))
                {
                    strName = string.Format("{0}{1}", fileName, System.IO.Path.GetExtension(file.FileName));
                }
                else
                {

                    strName = string.Format("{0}{1}", DateTime.Now.ToFileTime().ToString() + "" + new Random().Next(1000, 9999), System.IO.Path.GetExtension(file.FileName));
                }
                if (!Directory.Exists(HttpContext.Current.Server.MapPath("~") + "Temp/"))
                {
                    Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~") + "Temp/");
                }
                string strCurPath = "Temp/" + strName,
                    strLocalFile = HttpContext.Current.Server.MapPath("~") + strCurPath;

                //-----普通上传
                if ((Width == 0 || Height == 0) && !this.Upload(file, strCurPath, false, strFileType, iFileLength, ref strInfo))
                {
                    return false;
                }
                //----压缩图片----
                if (Width > 0 && Height > 0)
                {

                    bool flag = CreateThumbnail(file, Width, Height, true, strLocalFile);
                    if (!flag)
                    {
                        strInfo = "压缩图片失败";
                        return false;
                    }
                }
                bool bFlag = ft.Upload(strLocalFile, strName, Path);
                if (bFlag)
                {
                    strInfo = string.Format("{0}{1}", Path, strName);
                }
                FileInfo fileInfo = new FileInfo(strLocalFile);
                fileInfo.Delete();
                return bFlag;
            }
            catch
            {
                strInfo = "上传异常";
            }
            return false;
        }
        /// <summary>
        /// 上传文件到ftp上
        /// </summary>
        /// <param name="file"></param>
        /// <param name="strFileType">用;号割开。例如：.jpg;.png;.gif</param>
        /// <param name="iFileLength"></param>
        /// <param name="Width"></param>
        /// <param name="Height"></param>
        /// <param name="Path"></param>
        /// <param name="strInfo"></param>
        /// <returns></returns>
        public bool UploadFTP(HttpPostedFile file, string strFileType, int iFileLength, int Width, int Height, string Path, string fileName, ref string strInfo)
        {
            HttpPostedFileBase hpfb = new HttpPostedFileWrapper(file) as HttpPostedFileBase;
            return UploadFTP(hpfb, strFileType, iFileLength, Width, Height, Path, fileName, ref strInfo);
        }
        public bool UploadFTP(HttpPostedFile file, string strFileType, int iFileLength, int Width, int Height, string Path, ref string strInfo)
        {
            return UploadFTP(file, strFileType, iFileLength, Width, Height, Path, string.Empty, ref strInfo);
        }

        #endregion

        #region 检测文件类型是否正确
        /// <summary>
        /// 检测文件类型是否正确
        /// </summary>
        /// <remarks>
        /// Created By :王彦忠  
        /// Created Date：2011-9-13
        /// Modify By:
        /// Modify Date:
        /// Modify Reason:
        /// </remarks>
        /// <param name="strExt">文件类型</param>
        /// <returns>True:满足条件 False:不符合条件</returns>
        public bool CheckFileType(string strExt)
        {
            foreach (string str in strFileExt.Trim(';').Split(';'))
            {
                if (str.Equals(strExt, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region 创建文件夹和获取保存文件的名字
        /// <summary>
        /// 创建文件夹和获取保存文件的名字
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="strPath">文件路径</param>
        /// <param name="strExt">文件扩展名</param>
        /// <param name="strFolder">文件夹路径</param>
        /// <param name="strFileName">文件名字</param>
        /// <returns>返回文件名</returns>
        public void CreateFolderAndGetFileName(string strPath, string strExt, ref string strFolder, ref string strFileName)
        {
            strPath = strPath.Trim('/');
            if (strPath.IndexOf('.') > 0)
            {
                strFileName = strPath.Substring(strPath.LastIndexOf('/') + 1);
                strFolder = strPath.Substring(0, strPath.LastIndexOf('/'));
            }
            else
            {
                strFileName = Guid.NewGuid() + strExt;
                strFolder = "/" + strPath + "/" + DateTime.Now.ToString("yyyy-MM-dd") + "/";
            }
            strPath = "/" + strFolder;
            strFolder = HttpContext.Current.Server.MapPath(strPath);
            if (!Directory.Exists(strFolder))
            {
                Directory.CreateDirectory(strFolder);
            }
        }

        #endregion

        Point point = new Point(0, 0); //图像从那个坐标点进行截取
        double wRate = 1, hRate = 1, setRate = 1;
        int newWidth = 0, newHeight = 0;
        /// <summary>
        /// 图像的缩放
        /// </summary>
        /// <param name="file">缩放文件</param>
        /// <param name="width">宽</param>
        /// <param name="height">高</param>
        /// <param name="isEqualScale">是否等比例缩放</param>
        /// <param name="name">缩放后存放的地址</param>
        /// <returns></returns>
        public bool CreateThumbnail(HttpPostedFileBase file, double width, double height, bool isEqualScale, string name)
        {
            try
            {
                System.Drawing.Image image = System.Drawing.Image.FromStream(file.InputStream);
                if (isEqualScale)
                {
                    if (image.Height > height)
                    {
                        hRate = height / image.Height;
                    }

                    if (image.Width > width)
                    {
                        wRate = width / image.Width;
                    }

                    if (wRate != 1 || hRate != 1)
                    {
                        if (wRate > hRate)
                        {
                            setRate = hRate;
                        }
                        else
                        {
                            setRate = wRate;
                        }
                    }

                    newWidth = (int)(image.Width * setRate);
                    newHeight = (int)(image.Height * setRate);
                    if (height > newHeight)
                    {
                        point.Y = Convert.ToInt32(height / 2 - newHeight / 2);
                    }
                    if (width > newWidth)
                    {
                        point.X = Convert.ToInt32(width / 2 - newWidth / 2);
                    }

                }
                Bitmap bit = new Bitmap((int)(width), (int)(height));
                Rectangle r = new Rectangle(point.X, point.Y, (int)(image.Width * setRate), (int)(image.Height * setRate));

                Graphics g = Graphics.FromImage(bit);
                g.Clear(Color.White);
                g.DrawImage(image, r);


                MemoryStream ms = new MemoryStream();
                bit.Save(ms, ImageFormat.Jpeg);
                byte[] bytes = ms.ToArray();
                string fileName = name;
                using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
                {
                    stream.Write(bytes, 0, bytes.Length);
                }
                bit.Dispose();
                ms.Dispose();
                image.Dispose();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }


    /// <summary>
    /// 上传进度类
    /// </summary>
    public class UploadProcessor
    {

        private byte[] _buffer;
        private byte[] _boundaryBytes;
        private byte[] _endHeaderBytes;
        private byte[] _endFileBytes;
        private byte[] _lineBreakBytes;
        private const string _lineBreak = "\r\n";
        private readonly Regex _filename =
        new Regex(@"Content-Disposition:\s*form-data\s*;\s*name\s*=\s*""file""\s*;\s*filename\s*=\s*""(.*)""",
        RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private readonly HttpWorkerRequest _workerRequest;
        public UploadProcessor(HttpWorkerRequest workerRequest)
        {
            _workerRequest = workerRequest;
        }
        public void StreamToDisk(IServiceProvider provider, Encoding encoding, string rootPath)
        {
            var buffer = new byte[8192];
            if (!_workerRequest.HasEntityBody())
            {
                return;
            }
            var total = _workerRequest.GetTotalEntityBodyLength();
            var preloaded = _workerRequest.GetPreloadedEntityBodyLength();
            var loaded = preloaded;
            SetByteMarkers(_workerRequest, encoding);
            var body = _workerRequest.GetPreloadedEntityBody();
            if (body == null) // IE normally does not preload 
            {
                body = new byte[8192];
                preloaded = _workerRequest.ReadEntityBody(body, body.Length);
                loaded = preloaded;
            }
            var text = encoding.GetString(body);
            var fileName = _filename.Matches(text)[0].Groups[1].Value;
            fileName = Path.GetFileName(fileName); // IE captures full user path; chop it 
            var path = Path.Combine(rootPath, fileName);
            var files = new List<string> { fileName };
            var stream = new FileStream(path, FileMode.Create);
            if (preloaded > 0)
            {
                stream = ProcessHeaders(body, stream, encoding, preloaded, files, rootPath);
            }
            // Used to force further processing (i.e. redirects) to avoid buffering the files again 
            var workerRequest = new StaticWorkerRequest(_workerRequest, body);
            var field = HttpContext.Current.Request.GetType().GetField("_wr", BindingFlags.NonPublic | BindingFlags.Instance);
            field.SetValue(HttpContext.Current.Request, workerRequest);
            if (!_workerRequest.IsEntireEntityBodyIsPreloaded())
            {
                var received = preloaded;
                while (total - received >= loaded && _workerRequest.IsClientConnected())
                {
                    loaded = _workerRequest.ReadEntityBody(buffer, buffer.Length);
                    stream = ProcessHeaders(buffer, stream, encoding, loaded, files, rootPath);
                    received += loaded;
                }
                var remaining = total - received;
                buffer = new byte[remaining];
                loaded = _workerRequest.ReadEntityBody(buffer, remaining);
                stream = ProcessHeaders(buffer, stream, encoding, loaded, files, rootPath);
            }
            stream.Flush();
            stream.Close();
            stream.Dispose();
        }
        private void SetByteMarkers(HttpWorkerRequest workerRequest, Encoding encoding)
        {
            var contentType = workerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
            var bufferIndex = contentType.IndexOf("boundary=") + "boundary=".Length;
            var boundary = String.Concat("--", contentType.Substring(bufferIndex));
            _boundaryBytes = encoding.GetBytes(string.Concat(boundary, _lineBreak));
            _endHeaderBytes = encoding.GetBytes(string.Concat(_lineBreak, _lineBreak));
            _endFileBytes = encoding.GetBytes(string.Concat(_lineBreak, boundary, "--", _lineBreak));
            _lineBreakBytes = encoding.GetBytes(string.Concat(_lineBreak + boundary + _lineBreak));
        }
        private FileStream ProcessHeaders(byte[] buffer, FileStream stream, Encoding encoding, int count, ICollection<string> files, string rootPath)
        {
            buffer = AppendBuffer(buffer, count);
            var startIndex = IndexOf(buffer, _boundaryBytes, 0);
            if (startIndex != -1)
            {
                var endFileIndex = IndexOf(buffer, _endFileBytes, 0);
                if (endFileIndex != -1)
                {
                    var precedingBreakIndex = IndexOf(buffer, _lineBreakBytes, 0);
                    if (precedingBreakIndex > -1)
                    {
                        startIndex = precedingBreakIndex;
                    }
                    endFileIndex += _endFileBytes.Length;
                    var modified = SkipInput(buffer, startIndex, endFileIndex, ref count);
                    stream.Write(modified, 0, count);
                }
                else
                {
                    var endHeaderIndex = IndexOf(buffer, _endHeaderBytes, 0);
                    if (endHeaderIndex != -1)
                    {
                        endHeaderIndex += _endHeaderBytes.Length;
                        var text = encoding.GetString(buffer);
                        var match = _filename.Match(text);
                        var fileName = match != null ? match.Groups[1].Value : null;
                        fileName = Path.GetFileName(fileName); // IE captures full user path; chop it 
                        if (!string.IsNullOrEmpty(fileName) && !files.Contains(fileName))
                        {
                            files.Add(fileName);
                            var filePath = Path.Combine(rootPath, fileName);
                            stream = ProcessNextFile(stream, buffer, count, startIndex, endHeaderIndex, filePath);
                        }
                        else
                        {
                            var modified = SkipInput(buffer, startIndex, endHeaderIndex, ref count);
                            stream.Write(modified, 0, count);
                        }
                    }
                    else
                    {
                        _buffer = buffer;
                    }
                }
            }
            else
            {
                stream.Write(buffer, 0, count);
            }
            return stream;
        }

        private static FileStream ProcessNextFile(FileStream stream, byte[] buffer, int count, int startIndex, int endIndex, string filePath)
        {
            var fullCount = count;
            var endOfFile = SkipInput(buffer, startIndex, count, ref count);
            stream.Write(endOfFile, 0, count);
            stream.Flush();
            stream.Close();
            stream.Dispose();
            stream = new FileStream(filePath, FileMode.Create);
            var startOfFile = SkipInput(buffer, 0, endIndex, ref fullCount);
            stream.Write(startOfFile, 0, fullCount);
            return stream;
        }
        private static int IndexOf(byte[] array, IList<byte> value, int startIndex)
        {
            var index = 0;
            var start = Array.IndexOf(array, value[0], startIndex);
            if (start == -1)
            {
                return -1;
            }
            while ((start + index) < array.Length)
            {
                if (array[start + index] == value[index])
                {
                    index++;
                    if (index == value.Count)
                    {
                        return start;
                    }
                }
                else
                {
                    start = Array.IndexOf(array, value[0], start + index);
                    if (start != -1)
                    {
                        index = 0;
                    }
                    else
                    {
                        return -1;
                    }
                }
            }
            return -1;
        }
        private static byte[] SkipInput(byte[] input, int startIndex, int endIndex, ref int count)
        {
            var range = endIndex - startIndex;
            var size = count - range;
            var modified = new byte[size];
            var modifiedCount = 0;
            for (var i = 0; i < input.Length; i++)
            {
                if (i >= startIndex && i < endIndex)
                {
                    continue;
                }
                if (modifiedCount >= size)
                {
                    break;
                }
                modified[modifiedCount] = input[i];
                modifiedCount++;
            }
            input = modified;
            count = modified.Length;
            return input;
        }
        private byte[] AppendBuffer(byte[] buffer, int count)
        {
            var input = new byte[_buffer == null ? buffer.Length : _buffer.Length + count];
            if (_buffer != null)
            {
                Buffer.BlockCopy(_buffer, 0, input, 0, _buffer.Length);
            }
            Buffer.BlockCopy(buffer, 0, input, _buffer == null ? 0 : _buffer.Length, count);
            _buffer = null;
            return input;
        }
    }

    public class StaticWorkerRequest : HttpWorkerRequest
    {
        readonly HttpWorkerRequest _request;
        private readonly byte[] _buffer;

        public StaticWorkerRequest(HttpWorkerRequest request, byte[] buffer)
        {
            _request = request;
            _buffer = buffer;
        }

        public override int ReadEntityBody(byte[] buffer, int size)
        {
            return 0;
        }

        public override int ReadEntityBody(byte[] buffer, int offset, int size)
        {
            return 0;
        }

        public override byte[] GetPreloadedEntityBody()
        {
            return _buffer;
        }

        public override int GetPreloadedEntityBody(byte[] buffer, int offset)
        {
            Buffer.BlockCopy(_buffer, 0, buffer, offset, _buffer.Length);
            return _buffer.Length;
        }

        public override int GetPreloadedEntityBodyLength()
        {
            return _buffer.Length;
        }

        public override int GetTotalEntityBodyLength()
        {
            return _buffer.Length;
        }

        public override string GetKnownRequestHeader(int index)
        {
            return index == HeaderContentLength
                       ? "0"
                       : _request.GetKnownRequestHeader(index);
        }

        #region Passthrough

        public override string GetUriPath()
        {
            return _request.GetUriPath();
        }

        public override string GetQueryString()
        {
            return _request.GetQueryString();
        }

        public override string GetRawUrl()
        {
            return _request.GetRawUrl();
        }

        public override string GetHttpVerbName()
        {
            return _request.GetHttpVerbName();
        }

        public override string GetHttpVersion()
        {
            return _request.GetHttpVersion();
        }

        public override string GetRemoteAddress()
        {
            return _request.GetRemoteAddress();
        }

        public override int GetRemotePort()
        {
            return _request.GetRemotePort();
        }

        public override string GetLocalAddress()
        {
            return _request.GetLocalAddress();
        }

        public override int GetLocalPort()
        {
            return _request.GetLocalPort();
        }

        public override void SendStatus(int statusCode, string statusDescription)
        {
            _request.SendStatus(statusCode, statusDescription);
        }

        public override void SendKnownResponseHeader(int index, string value)
        {
            _request.SendKnownResponseHeader(index, value);
        }

        public override void SendUnknownResponseHeader(string name, string value)
        {
            _request.SendUnknownResponseHeader(name, value);
        }

        public override void SendResponseFromMemory(byte[] data, int length)
        {
            _request.SendResponseFromMemory(data, length);
        }

        public override void SendResponseFromFile(string filename, long offset, long length)
        {
            _request.SendResponseFromFile(filename, offset, length);
        }

        public override void SendResponseFromFile(IntPtr handle, long offset, long length)
        {
            _request.SendResponseFromFile(handle, offset, length);
        }

        public override bool IsEntireEntityBodyIsPreloaded()
        {
            return true;
        }

        public override void FlushResponse(bool finalFlush)
        {
            _request.FlushResponse(finalFlush);
        }

        public override void EndOfRequest()
        {
            _request.EndOfRequest();
        }

        public override void CloseConnection()
        {
            _request.CloseConnection();
        }
        #endregion
    }
}
